/**
 * Main for the p2p-system 
 **/

#include <iostream>
#include <string>


#include "Peer/AbstractPeer.h"
#include "Peer/Peer2Peer-Peer/P2pPeer.h"
#include "Peer/PeerTestingFramework/PeerTestingFramework.h"
#include  "Peer/Neighbors/Neighbour.h"
#include "Peer/NeighbourManager/NeighboursManager.h"
#include "UtilityClasses/HelperClass.h"

#include "TCP/TestAsio/Server/AsioSimpleServer.h"
#include "TCP/TestAsio/Client/AsioClient.h"

using std::string;
using std::cout;
using std::endl;
using std::cin;

//int main(int arg, char *argv[])
//{
//	//string help = "help";	
//	//if (arg == 1)
//	//{
//	//	//acts as a gate keeper
//	//	Network::Peer::AbstarctPeer<string>* testPeer = new Network::Peer::P2pPeer<string>();
//	//	cin.ignore();
//	//}
//	//else if (arg > 1)
//	//{	
//		/*char *command = argv[1];
//		cout << command << endl;
//		if (0 == strcmp(help.c_str(), command))
//		{
//			cout << "please enter the ip address alone" << endl;
//			cout << "Or enter in conjunction to the index of the peer" << endl;
//		}
//		else*/
//		//{
//			if (arg <= 1)
//			{
//				/*string ipAddress = argv[1];
//				string name = argv[2];
//				Network::Peer::Testing::TestingFramework<string> *currentPeer = new Network::Peer::Testing::TestingFramework<string>(ipAddress, 100, name);*/
//
//				Network::Communication::SimpleTestServer testServer;
//
//			}
//			else if (arg > 1)
//			{
//				/*string ipAddress = argv[1];
//				string name = argv[4];
//				int socketNumber = std::stoi(string(argv[2]));
//				int sequenceNumber = std::stoi(string(argv[3]));
//*/
//				//Network::Peer::AbstarctPeer<string> *currentPeer = new Network::Peer::P2pPeer<string>(ipAddress, socketNumber,sequenceNumber, name);
//				////currentPeer->RunUpdateProcedure();
//				Network::Communication::TestClient testClient;
//				system("pause");
//			}
//		//}
//		
//	//}
//}

//#define BOOST_TEST_MODULE MyTest
//#include <algorithm>
//#include <boost/test/unit_test.hpp>
//#include <boost/test/unit_test_suite.hpp>
//#include <boost/test/unit_test_log.hpp>

///// <summary>
///// Main entry-point for this application.
///// </summary>
///// <param name="arg">Number of command-line arguments.</param>
///// <param name="argv">Array of command-line argument strings.</param>
///// <returns>
///// Exit-code for the process - 0 for success, else an error code.
///// </returns>
///// <remarks>
///// Adrian, 31/08/2015.
///// </remarks>
///// -------------------------------------------------------------------------------------------------
///// -------------------------------------------------------------------------------------------------
//
///*
//--------------------------------------
//Testing Peer neighbour handling
//--------------------------------------
//*/
//
//using namespace Network::Peer;
//using namespace RakNet;
//
//BOOST_AUTO_TEST_CASE(My_Test)
//{
//	NeighbourManager* manager = new NeighbourManager();
//	std::string inputNeighbours = "11223-127.0.0.1|21112-0;112356-124.0.0.12|2115|0;11223-127.0.0.1|21112|0;";
//	manager->AddNewNeighbours(inputNeighbours);
//	/*testing neighbours size == 2*/
//	BOOST_CHECK(manager->GetAmountPeers() == 2);
//	std::string toFind = "127.0.0.1|21112";
//	int loc = 0;
//	BOOST_CHECK(manager->FindNeighbour(SystemAddress(toFind.c_str())) == true);
//	manager->RemoveNeighbour(SystemAddress(toFind.c_str()));
//	BOOST_CHECK(manager->FindNeighbour(SystemAddress(toFind.c_str())) == false);
//
//	Neighbour* randomNeighbor = nullptr;
//	randomNeighbor = manager->GetRandomNeighbour();
//	BOOST_CHECK(randomNeighbor != nullptr);
//
//	SystemAddress address;
//	address.FromString("124.0.0.12|2115");
//	manager->RemoveNeighbour(address);
//
//	BOOST_CHECK(manager->FindNeighbour(SystemAddress(string("124.0.0.12|2115").c_str())) == false);
//
//	inputNeighbours = "11223-127.0.0.1|21112|0;112356-124.0.0.12|2115|0;11223-127.0.0.1|21112|0;114545-129.0.0.2|45669|0;1129-189.168.2.1|44568|0;";
//	manager->AddNewNeighbours(inputNeighbours);
//	//sort 
//
//	/*int previous = 0, current = 0;
//
//	for (int index = 0; index < testingPeer->neighbors.size(); index++)
//	{
//	previous = index == 0 ? 0 : testingPeer->neighbors[index - 1].lastConnection;
//	current = testingPeer->neighbors[index].lastConnection;
//	BOOST_CHECK(previous <= current);
//	}
//
//	std::string output[2];*/
//}
//
//BOOST_AUTO_TEST_CASE(TestingVectorTimer)
//{
//	//one observable event
//	std::string inputString = "1,0,0,0,0";
//	int indexPosition = 1;
//	FixedVectorTimer test(1);
//	test.FromString(inputString);
//
//	std::string testOuptut = test.ToString();
//	BOOST_CHECK(testOuptut == inputString);
//
//	test.UpdatePeerTimer(5);
//	BOOST_CHECK(test.ToString() == "1,5,0,0,0");
//	//equality == true
//	BOOST_CHECK((test == test) == true);
//
//	std::string stringSecondInput = "1,2,3,1,0";
//	FixedVectorTimer secondTest(1);
//	secondTest.FromString(stringSecondInput);
//	bool equalityTest = (secondTest == test);
//	//equality should be false
//	BOOST_CHECK(equalityTest, false);
//	
//}
//
//#include "ObservableEvents/ObservableEvent.h"
//
//BOOST_AUTO_TEST_CASE(TestingObservableEvent)
//{
//	std::string inputString = "1,0,0,0,0";
//	int indexPosition = 1;
//	Network::VectorTimer* testVectorTimer = new FixedVectorTimer(1);
//	testVectorTimer->FromString(inputString);
//
//	Network::ObservableEvents::ObservableEvent<string>* testObservableEvent = new Network::ObservableEvents::ObservableEvent<string>(testVectorTimer, "a", 0);
//	
//	BOOST_CHECK("a", testObservableEvent->GetStringUpdate());
//	string tetsObservableEventOutput = testObservableEvent->ToString();
//	Network::ObservableEvents::ObservableEvent<string>* secondObservableEvent = new Network::ObservableEvents::ObservableEvent<string>();
//	secondObservableEvent->FromString(tetsObservableEventOutput);
//	cout << tetsObservableEventOutput << endl;
//	cout << secondObservableEvent->ToString() << endl;
//	BOOST_CHECK((secondObservableEvent == testObservableEvent) == true);
//
//	//BOOST_CHECK_EQUAL(tetsObservableEventOutput, secondObservableEvent->ToString());
//
//	system("pause");
//}
//
//BOOST_AUTO_TEST_CASE(TestingObservableEventsList)
//{
//	
//}


#pragma region 

///*
//*  Copyright (c) 2014, Oculus VR, Inc.
//*  All rights reserved.
//*
//*  This source code is licensed under the BSD-style license found in the
//*  LICENSE file in the root directory of this source tree. An additional grant
//*  of patent rights can be found in the PATENTS file in the same directory.
//*
//*/
//
//// ----------------------------------------------------------------------
//// RakNet version 1.0
//// Filename ChatExample.cpp
//// Very basic chat engine example
//// ----------------------------------------------------------------------
//
//#include "Debug\MessageIdentifiers.h"
//
//#include "Debug\RakPeerInterface.h"
//#include "Debug\RakNetStatistics.h"
//#include "Debug\RakNetTypes.h"
//#include "Debug\BitStream.h"
//#include "Debug\RakSleep.h"
//#include "Debug\PacketLogger.h"
//#include <assert.h>
//#include <cstdio>
//#include <cstring>
//#include <stdlib.h>
//#include "Debug\Kbhit.h"
//#include <stdio.h>
//#include <string.h>
//#include "Debug\Gets.h"
//
//#if LIBCAT_SECURITY==1
//#include "SecureHandshake.h" // Include header for secure handshake
//#endif
//
//#if defined(_CONSOLE_2)
//#include "Console2SampleIncludes.h"
//#endif
//
//// We copy this from Multiplayer.cpp to keep things all in one file for this example
//unsigned char GetPacketIdentifier(RakNet::Packet *p);
//
//#ifdef _CONSOLE_2
//_CONSOLE_2_SetSystemProcessParams
//#endif
//
//int main(void)
//{
//	// Pointers to the interfaces of our server and client.
//	// Note we can easily have both in the same program
//	RakNet::RakPeerInterface *server = RakNet::RakPeerInterface::GetInstance();
//	RakNet::RakNetStatistics *rss;
//	server->SetIncomingPassword("Rumpelstiltskin", (int)strlen("Rumpelstiltskin"));
//	server->SetTimeoutTime(30000, RakNet::UNASSIGNED_SYSTEM_ADDRESS);
//	//	RakNet::PacketLogger packetLogger;
//	//	server->AttachPlugin(&packetLogger);
//
//#if LIBCAT_SECURITY==1
//	cat::EasyHandshake handshake;
//	char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES];
//	char private_key[cat::EasyHandshake::PRIVATE_KEY_BYTES];
//	handshake.GenerateServerKey(public_key, private_key);
//	server->InitializeSecurity(public_key, private_key, false);
//	FILE *fp = fopen("publicKey.dat", "wb");
//	fwrite(public_key, sizeof(public_key), 1, fp);
//	fclose(fp);
//#endif
//
//	// Holds packets
//	RakNet::Packet* p;
//
//	// GetPacketIdentifier returns this
//	unsigned char packetIdentifier;
//
//	// Record the first client that connects to us so we can pass it to the ping function
//	RakNet::SystemAddress clientID = RakNet::UNASSIGNED_SYSTEM_ADDRESS;
//
//	// Holds user data
//	char portstring[30];
//
//	printf("This is a sample implementation of a text based chat server.\n");
//	printf("Connect to the project 'Chat Example Client'.\n");
//	printf("Difficulty: Beginner\n\n");
//
//	// A server
//	puts("Enter the server port to listen on");
//	Gets(portstring, sizeof(portstring));
//	if (portstring[0] == 0)
//		strcpy(portstring, "1234");
//
//	puts("Starting server.");
//	// Starting the server is very simple.  2 players allowed.
//	// 0 means we don't care about a connectionValidationInteger, and false
//	// for low priority threads
//	// I am creating two socketDesciptors, to create two sockets. One using IPV6 and the other IPV4
//	RakNet::SocketDescriptor socketDescriptors[2];
//	socketDescriptors[0].port = atoi(portstring);
//	socketDescriptors[0].socketFamily = AF_INET; // Test out IPV4
//	socketDescriptors[1].port = atoi(portstring);
//	socketDescriptors[1].socketFamily = AF_INET6; // Test out IPV6
//	bool b = server->Startup(4, socketDescriptors, 2) == RakNet::RAKNET_STARTED;
//	server->SetMaximumIncomingConnections(4);
//	if (!b)
//	{
//		printf("Failed to start dual IPV4 and IPV6 ports. Trying IPV4 only.\n");
//
//		// Try again, but leave out IPV6
//		b = server->Startup(4, socketDescriptors, 1) == RakNet::RAKNET_STARTED;
//		if (!b)
//		{
//			puts("Server failed to start.  Terminating.");
//			exit(1);
//		}
//	}
//	server->SetOccasionalPing(true);
//	server->SetUnreliableTimeout(1000);
//
//	DataStructures::List< RakNet::RakNetSocket2* > sockets;
//	server->GetSockets(sockets);
//	printf("Socket addresses used by RakNet:\n");
//	for (unsigned int i = 0; i < sockets.Size(); i++)
//	{
//		printf("%i. %s\n", i + 1, sockets[i]->GetBoundAddress().ToString(true));
//	}
//
//	printf("\nMy IP addresses:\n");
//	for (unsigned int i = 0; i < server->GetNumberOfAddresses(); i++)
//	{
//		RakNet::SystemAddress sa = server->GetInternalID(RakNet::UNASSIGNED_SYSTEM_ADDRESS, i);
//		printf("%i. %s (LAN=%i)\n", i + 1, sa.ToString(false), sa.IsLANAddress());
//	}
//
//	printf("\nMy GUID is %s\n", server->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
//	puts("'quit' to quit. 'stat' to show stats. 'ping' to ping.\n'pingip' to ping an ip address\n'ban' to ban an IP from connecting.\n'kick to kick the first connected player.\nType to talk.");
//	char message[2048];
//
//	// Loop for input
//	while (1)
//	{
//
//		// This sleep keeps RakNet responsive
//		RakSleep(30);
//
//		if (_kbhit)
//		{
//			// Notice what is not here: something to keep our network running.  It's
//			// fine to block on gets or anything we want
//			// Because the network engine was painstakingly written using threads.
//			Gets(message, sizeof(message));
//
//			if (strcmp(message, "quit") == 0)
//			{
//				puts("Quitting.");
//				break;
//			}
//
//			if (strcmp(message, "stat") == 0)
//			{
//				rss = server->GetStatistics(server->GetSystemAddressFromIndex(0));
//				StatisticsToString(rss, message, 2);
//				printf("%s", message);
//				printf("Ping %i\n", server->GetAveragePing(server->GetSystemAddressFromIndex(0)));
//
//				continue;
//			}
//
//			if (strcmp(message, "ping") == 0)
//			{
//				server->Ping(clientID);
//
//				continue;
//			}
//
//			if (strcmp(message, "pingip") == 0)
//			{
//				printf("Enter IP: ");
//				Gets(message, sizeof(message));
//				printf("Enter port: ");
//				Gets(portstring, sizeof(portstring));
//				if (portstring[0] == 0)
//					strcpy(portstring, "1234");
//				server->Ping(message, atoi(portstring), false);
//
//				continue;
//			}
//
//			if (strcmp(message, "kick") == 0)
//			{
//				server->CloseConnection(clientID, true, 0);
//
//				continue;
//			}
//
//			if (strcmp(message, "getconnectionlist") == 0)
//			{
//				RakNet::SystemAddress systems[10];
//				unsigned short numConnections = 10;
//				server->GetConnectionList((RakNet::SystemAddress*) &systems, &numConnections);
//				for (int i = 0; i < numConnections; i++)
//				{
//					printf("%i. %s\n", i + 1, systems[i].ToString(true));
//				}
//				continue;
//			}
//
//			if (strcmp(message, "ban") == 0)
//			{
//				printf("Enter IP to ban.  You can use * as a wildcard\n");
//				Gets(message, sizeof(message));
//				server->AddToBanList(message);
//				printf("IP %s added to ban list.\n", message);
//
//				continue;
//			}
//
//
//			// Message now holds what we want to broadcast
//			char message2[2048];
//			// Append Server: to the message so clients know that it ORIGINATED from the server
//			// All messages to all clients come from the server either directly or by being
//			// relayed from other clients
//			message2[0] = 0;
//			const static char prefix[] = "Server: ";
//			strncpy(message2, prefix, sizeof(message2));
//			strncat(message2, message, sizeof(message2)-strlen(prefix) - 1);
//
//			// message2 is the data to send
//			// strlen(message2)+1 is to send the null terminator
//			// HIGH_PRIORITY doesn't actually matter here because we don't use any other priority
//			// RELIABLE_ORDERED means make sure the message arrives in the right order
//			// We arbitrarily pick 0 for the ordering stream
//			// RakNet::UNASSIGNED_SYSTEM_ADDRESS means don't exclude anyone from the broadcast
//			// true means broadcast the message to everyone connected
//			server->Send(message2, (const int)strlen(message2) + 1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
//		}
//
//		// Get a packet from either the server or the client
//
//		for (p = server->Receive(); p; server->DeallocatePacket(p), p = server->Receive())
//		{
//			// We got a packet, get the identifier with our handy function
//			packetIdentifier = GetPacketIdentifier(p);
//
//			// Check if this is a network message packet
//			switch (packetIdentifier)
//			{
//			case ID_DISCONNECTION_NOTIFICATION:
//				// Connection lost normally
//				printf("ID_DISCONNECTION_NOTIFICATION from %s\n", p->systemAddress.ToString(true));;
//				break;
//
//
//			case ID_NEW_INCOMING_CONNECTION:
//				// Somebody connected.  We have their IP now
//				printf("ID_NEW_INCOMING_CONNECTION from %s with GUID %s\n", p->systemAddress.ToString(true), p->guid.ToString());
//				clientID = p->systemAddress; // Record the player ID of the client
//
//				printf("Remote internal IDs:\n");
//				for (int index = 0; index < MAXIMUM_NUMBER_OF_INTERNAL_IDS; index++)
//				{
//					RakNet::SystemAddress internalId = server->GetInternalID(p->systemAddress, index);
//					if (internalId != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
//					{
//						printf("%i. %s\n", index + 1, internalId.ToString(true));
//					}
//				}
//
//				break;
//
//			case ID_INCOMPATIBLE_PROTOCOL_VERSION:
//				printf("ID_INCOMPATIBLE_PROTOCOL_VERSION\n");
//				break;
//
//			case ID_CONNECTED_PING:
//			case ID_UNCONNECTED_PING:
//				printf("Ping from %s\n", p->systemAddress.ToString(true));
//				break;
//
//			case ID_CONNECTION_LOST:
//				// Couldn't deliver a reliable packet - i.e. the other system was abnormally
//				// terminated
//				printf("ID_CONNECTION_LOST from %s\n", p->systemAddress.ToString(true));;
//				break;
//
//			default:
//				// The server knows the static data of all clients, so we can prefix the message
//				// With the name data
//				printf("%s\n", p->data);
//
//				// Relay the message.  We prefix the name for other clients.  This demonstrates
//				// That messages can be changed on the server before being broadcast
//				// Sending is the same as before
//				sprintf(message, "%s", p->data);
//				server->Send(message, (const int)strlen(message) + 1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, p->systemAddress, true);
//
//				break;
//			}
//
//		}
//	}
//
//	server->Shutdown(300);
//	// We're done with the network
//	RakNet::RakPeerInterface::DestroyInstance(server);
//
//	return 0;
//}
//
//// Copied from Multiplayer.cpp
//// If the first byte is ID_TIMESTAMP, then we want the 5th byte
//// Otherwise we want the 1st byte
//unsigned char GetPacketIdentifier(RakNet::Packet *p)
//{
//	if (p == 0)
//		return 255;
//
//	if ((unsigned char)p->data[0] == ID_TIMESTAMP)
//	{
//		RakAssert(p->length > sizeof(RakNet::MessageID) + sizeof(RakNet::Time));
//		return (unsigned char)p->data[sizeof(RakNet::MessageID) + sizeof(RakNet::Time)];
//	}
//	else
//		return (unsigned char)p->data[0];
//}
#pragma endregion old sample code